/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ohosannotations.internal.core.handler;

import com.helger.jcodemodel.IJExpression;
import com.helger.jcodemodel.JBlock;
import com.helger.jcodemodel.JExpr;
import com.helger.jcodemodel.JInvocation;
import com.helger.jcodemodel.JOp;
import com.helger.jcodemodel.JVar;

import org.ohosannotations.ElementValidation;
import org.ohosannotations.OhosAnnotationsEnvironment;
import org.ohosannotations.annotations.OnAbilityResult;
import org.ohosannotations.handler.AnnotationHandler;
import org.ohosannotations.handler.BaseAnnotationHandler;
import org.ohosannotations.handler.HasParameterHandlers;
import org.ohosannotations.helper.CanonicalNameConstants;
import org.ohosannotations.holder.HasOnAbilityResult;

import java.util.Collections;
import java.util.List;

import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;

import static com.helger.jcodemodel.JExpr._new;
import static com.helger.jcodemodel.JExpr._null;

/**
 * 在能力结果处理程序
 *
 * @author dev
 * @since 2021-06-03
 */
public class OnAbilityResultHandler extends BaseAnnotationHandler<HasOnAbilityResult>
    implements HasParameterHandlers<HasOnAbilityResult> {
    private ExtraHandler extraHandler;

    /**
     * 在能力结果处理程序
     *
     * @param environment 环境
     */
    public OnAbilityResultHandler(OhosAnnotationsEnvironment environment) {
        super(OnAbilityResult.class, environment);
        extraHandler = new ExtraHandler(environment);
    }

    /**
     * 得到参数处理程序
     *
     * @return {@link Iterable<AnnotationHandler>}
     */
    @Override
    public Iterable<AnnotationHandler> getParameterHandlers() {
        return Collections.<AnnotationHandler>singleton(extraHandler);
    }

    /**
     * 验证
     *
     * @param element 元素
     * @param validation 验证
     */
    @Override
    public void validate(Element element, ElementValidation validation) {
        validatorHelper.enclosingElementHasEAbilityOrEFraction(element, validation);

        validatorHelper.isNotPrivate(element, validation);

        validatorHelper.doesntThrowException(element, validation);

        OnAbilityResult onResultAnnotation = element.getAnnotation(OnAbilityResult.class);
        validatorHelper.annotationValuePositiveAndInAShort(onResultAnnotation.value(), validation);

        ExecutableElement executableElement = (ExecutableElement) element;
        validatorHelper.returnTypeIsVoid(executableElement, validation);

        validatorHelper.param.anyOrder() //
            .type(CanonicalNameConstants.INTENT).optional() //
            .primitiveOrWrapper(TypeKind.INT).optional() //
            .annotatedWith(OnAbilityResult.Extra.class).multiple().optional() //
            .validate((ExecutableElement) element, validation); //
    }

    /**
     * 过程
     *
     * @param element 元素
     * @param holder 持有人
     * @throws Exception 异常
     */
    @Override
    public void process(Element element, HasOnAbilityResult holder) throws Exception {
        String methodName = element.getSimpleName().toString();

        ExecutableElement executableElement = (ExecutableElement) element;
        List<? extends VariableElement> parameters = executableElement.getParameters();

        int requestCode = executableElement.getAnnotation(OnAbilityResult.class).value();

        JBlock onResultBlock = holder.getOnAbilityResultCaseBlock(requestCode).blockSimple();

        IJExpression abilityRef = holder.getGeneratedClass().staticRef("this");
        JInvocation onResultInvocation = JExpr.invoke(abilityRef, methodName);

        JVar intent = holder.getOnAbilityResultDataParam();
        JVar extras = null;

        for (VariableElement parameter : parameters) {
            TypeMirror parameterType = parameter.asType();
            if (parameter.getAnnotation(OnAbilityResult.Extra.class) != null) {
                if (extras == null) {
                    extras = onResultBlock.decl(getClasses().INTENT_PARAMS, "params_",
                        JOp.cond(intent.ne(_null()).cand(intent.invoke("getParams")
                            .ne(_null())), intent.invoke("getParams"), _new(getClasses().INTENT_PARAMS)));
                }
                IJExpression extraParameter = extraHandler.getExtraValue(parameter, extras, onResultBlock, holder);
                onResultInvocation.arg(extraParameter);
            } else if (CanonicalNameConstants.INTENT.equals(parameterType.toString())) {
                onResultInvocation.arg(intent);
            } else if (parameterType.getKind().equals(TypeKind.INT)
                || CanonicalNameConstants.INTEGER.equals(parameterType.toString())) {
                onResultInvocation.arg(holder.getOnAbilityResultResultCodeParam());
            } else {
            }
        }
        onResultBlock.add(onResultInvocation);
    }

    /**
     * 额外的处理程序
     *
     * @author dev
     * @since 2021-07-23
     */
    private static class ExtraHandler extends ExtraParameterHandler {
        ExtraHandler(OhosAnnotationsEnvironment environment) {
            super(OnAbilityResult.Extra.class, OnAbilityResult.class, environment);
        }

        /**
         * getAnnotationValue
         *
         * @param parameter parameter
         * @return parameter
         */
        @Override
        public String getAnnotationValue(VariableElement parameter) {
            return parameter.getAnnotation(OnAbilityResult.Extra.class).value();
        }

        /**
         * getExtraValue
         *
         * @param parameter parameter
         * @param extras extras
         * @param block block
         * @param holder holder
         * @return super
         */
        public IJExpression getExtraValue(VariableElement parameter,
            JVar extras, JBlock block, HasOnAbilityResult holder) {
            return super.getExtraValue(parameter, extras,
                block, holder.getOnAbilityResultMethod(), holder);
        }
    }
}
